package com.mallcai.bigdata.ladon.service.export.impl.handler;

import com.cake.bigdata.starter.exception.category.BusinessException;
import com.google.common.base.Preconditions;
import com.mallcai.bigdata.ladon.client.export.oridata.DirectConnectDbOriData;
import com.mallcai.bigdata.ladon.client.vo.ReportColumnVO;
import com.mallcai.bigdata.ladon.client.vo.ReportDataVO;
import com.mallcai.bigdata.ladon.datasource.AbstractDataSource;
import com.mallcai.bigdata.ladon.datasource.DatasourceFactory;
import com.mallcai.bigdata.ladon.datasource.ResultSetDataTool;
import com.mallcai.bigdata.ladon.dpl.entity.common.Datasource;
import com.mallcai.bigdata.ladon.dpl.vo.DataType;
import com.mallcai.bigdata.ladon.service.common.IDatasourceService;
import com.mallcai.bigdata.ladon.service.export.impl.ExportContext;
import com.mallcai.bigdata.ladon.vo.Pair;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * described :将DirectConnectDbOriData类型的数据源转化为ReportDataVO
 *
 * @author CaKeDou
 * @date 2019-09-26 21:52
 * Modify:
 */
@Slf4j
@Component
@Order(500)
public class ResultSetConvertHandler extends AbstractExportPreHandler {
    @Autowired
    private IDatasourceService iDatasourceService;

    @Override
    public void preInternal(ExportContext exportContext) {
        if (!(exportContext.getOriData() instanceof DirectConnectDbOriData)) {
            return;
        }
        DirectConnectDbOriData oriData = (DirectConnectDbOriData) exportContext.getOriData();
        Long datasourceId = oriData.getDatasourceId();
        Datasource ds = Preconditions.checkNotNull(iDatasourceService.getById(datasourceId), "数据源信息不存在");
        AbstractDataSource dataSource = DatasourceFactory.getDataSource(ds);
        Connection connection = dataSource.getConnection();
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            statement = getStatement(connection, new Pair<>(oriData.getSql(), oriData.getParam()));
            resultSet = getResultSet(statement, oriData.getSql());
            List<ReportColumnVO> columnVos = getColumns(resultSet, oriData.getShowNames());
            List<Map<String, Object>> rows = getRows(resultSet, columnVos);
            ReportDataVO reportDataVO = new ReportDataVO(columnVos, rows, rows.size());
            exportContext.setTargetData(reportDataVO);
        } catch (Exception e) {
            log.error("ResultSet数据集转换出错", e);
            throw new BusinessException("ResultSet数据集转换出错");
        } finally {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (statement != null) {
                    statement.close();
                }
                connection.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }


    private List<ReportColumnVO> getColumns(ResultSet rsData, Map<String, String> showNames) throws SQLException {
        List<ReportColumnVO> columns = new ArrayList<>();
        ResultSetMetaData resultSetMetaData = rsData.getMetaData();
        for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++) {
            String name = resultSetMetaData.getColumnLabel(i);
            DataType type = ResultSetDataTool.getDataType(resultSetMetaData.getColumnClassName(i));
            ReportColumnVO columnVO = new ReportColumnVO();
            columnVO.setName(name);
            columnVO.setDataType(type.getCode());
            columnVO.setShowName(showNames.get(name));
            columns.add(columnVO);
        }
        return columns;
    }

    private List<Map<String, Object>> getRows(ResultSet rsData, List<ReportColumnVO> columnVos) throws Exception {
        List<Map<String, Object>> rows = new ArrayList<>();
        while (rsData.next()) {
            Map<String, Object> oneRow = new HashMap<>();
            for (ReportColumnVO column : columnVos) {
                Object value = ResultSetDataTool.getValue(rsData, column.getName(), DataType.getByCodeWithDefault(column.getDataType(), DataType.STRING));
                oneRow.put(column.getName(), value);
            }
            rows.add(oneRow);
        }
        return rows;
    }

    private Statement getStatement(Connection conn, Pair<String, Map<Integer, Object>> dataSql) throws Exception {

        if (dataSql == null || dataSql.getLeft() == null) {
            return null;
        }
        try {
            PreparedStatement preStmt = conn.prepareStatement(addLimit(dataSql.getLeft()));
            //写入预编译时的参数
            if (dataSql.getRight() != null && dataSql.getRight().size() > 0) {
                Object obj;
                String objStr;
                for (Integer key : dataSql.getRight().keySet()) {
                    obj = dataSql.getRight().get(key);
                    objStr = String.valueOf(obj);
                    if (obj instanceof String) { //字符串
                        preStmt.setString(key, objStr);
                    } else if (obj instanceof Integer) { //整数类型
                        preStmt.setInt(key, Integer.parseInt(objStr));
                    } else if (obj instanceof Boolean) { //布尔类型
                        preStmt.setBoolean(key, Boolean.valueOf(objStr));
                    } else if (obj instanceof Double) {
                        preStmt.setDouble(key, Double.valueOf(objStr));
                    } else {
                        preStmt.setObject(key, obj);
                    }
                }
            }
            return preStmt;
        } catch (Exception e) {
            return conn.createStatement();
        }
    }

    private ResultSet getResultSet(Statement statement, String sql) throws SQLException {
        ResultSet rsData;
        //执行sql
        if (statement instanceof PreparedStatement) {
            PreparedStatement tmp = (PreparedStatement) statement;
            rsData = tmp.executeQuery();
        } else {
            rsData = statement.executeQuery(sql);
        }
        return rsData;
    }

    private static final int    MAX_SIZE  = 70 * 10000;
    private static final String KEY_LIMIT = " LIMIT ";

    private String addLimit(String sql) {
        //特殊符号判断
        sql = StringUtils.trim(sql);
        if (StringUtils.endsWith(sql, ";")) {
            sql = StringUtils.substring(sql, 0, sql.length() - 1); //去除最后一个";"
        }

        //判断是否有LIMIT存在
        if (StringUtils.indexOf(StringUtils.upperCase(sql), KEY_LIMIT) <= 0) {
            sql = sql + KEY_LIMIT + (MAX_SIZE + 1); //这里多加一条用于判断是否还有更多的数据
        }
        return sql;
    }
}
